// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.ads.tools.jaxws;

import com.google.common.annotations.VisibleForTesting;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

/**
 * An object representing a JAX-WS bindings file to be used by the wsimport
 * tool.
 *
 * @author Joseph DiLallo
 */
public class BindingsFile {

  private Writer writer;

  @VisibleForTesting
  static final String BINDING_FILE_HEADER = "<jaxws:bindings xmlns:jaxb=\""
      + "http://java.sun.com/xml/ns/jaxb\" xmlns:jaxws=\"http://java.sun.com/xml/ns/jaxws\" "
      + "xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:xsd=\""
      + "http://www.w3.org/2001/XMLSchema\">\n";
  @VisibleForTesting
  static final String SCHEMA_PACKAGE_BINDING = "<jaxws:bindings node=\""
      + "wsdl:definitions/wsdl:types/xsd:schema[@targetNamespace ='%s']\">\n"
      + "<jaxb:schemaBindings>\n<jaxb:package name=\"%s\"/>\n"
      + "</jaxb:schemaBindings>\n</jaxws:bindings>\n";
  @VisibleForTesting
  static final String NON_SCHEMA_PACKAGE_BINDING = "<jaxws:package name=\""
      + "%s\" />\n";
  @VisibleForTesting
  static final String ELEMENT_BINDING = "<jaxws:bindings node=\""
      + "wsdl:definitions/wsdl:types/xsd:schema/xsd:element[@name='%s']\">\n"
      + "<jaxb:class name=\"%s\" />\n</jaxws:bindings>\n";
  @VisibleForTesting
  static final String BINDING_FILE_FOOTER = "</jaxws:bindings>\n";

  /**
   * Constructor. Creates a file on the file system.
   *
   * @param fileName The file name of the desired bindings file
   * @throws IOException if the file could not be created
   */
  public BindingsFile(String fileName) throws IOException {
    this(new FileWriter(new File(fileName)));
  }

  /**
   * Testable constructor. Takes in a {@code Writer} instead of creating its
   * own.
   *
   * @param writer The object which will write the bindings file.
   */
  @VisibleForTesting
  BindingsFile(Writer writer) {
    this.writer = writer;
  }

  /**
   * Writes the binding file header.
   *
   * @throws IOException if the binding file cannot be written to
   */
  public void writeHeader() throws IOException {
    writer.write(BINDING_FILE_HEADER);
  }

  /**
   * Writes a binding to change the package of generated class not associated
   * with a schema definition.
   *
   * @param targetPackage The package name to use for non-schema classes
   * @throws IOException if the in-progress file could not be written to
   */
  public void writeNonSchemaPackageBinding(String targetPackage) throws IOException {
    writer.write(String.format(NON_SCHEMA_PACKAGE_BINDING, targetPackage));
  }

  /**
   * Writes a binding to change the name of the generated class for the
   * specified element.
   *
   * @param oldElementName The element name to change the class name for
   * @param newElementName The class name to use for this element
   * @throws IOException if the in-progress file could not be written to
   */
  public void writeElementBinding(String oldElementName, String newElementName)
      throws IOException {
    writer.write(String.format(ELEMENT_BINDING, oldElementName, newElementName));
  }

  /**
   * Writes a binding to change the package of generated class associated
   * with a schema definition.
   *
   * @param targetNamespace The namespace of the schema to change the package
   *        name of
   * @param packageName The package name to use for generated classes in this
   *        schema
   * @throws IOException if the in-progress file could not be written to
   */
  public void writeSchemaPackageBinding(String targetNamespace, String packageName)
      throws IOException {
    writer.write(String.format(SCHEMA_PACKAGE_BINDING, targetNamespace, packageName));
  }

  /**
   * Writes the necessary footer to a bindings file and closes the writer.
   *
   * @throws IOException if the file could not be written to or closed
   */
  public void close() throws IOException {
    writer.write(BINDING_FILE_FOOTER);
    writer.close();
  }
}
